打造專屬 ChatGPT:從 Chat Completions 轉向 Responses API (20260601更新)

A. 前言:為什麼現在要看 Responses API?

前面幾篇文章已經完整整理過 Chat Completions API。
如果你正在維護既有專案,尤其是用 client.chat.completions.create(...) 建立的聊天、客服、摘要或 Tool Calling 功能,理解 Chat Completions API 仍然很重要。

很多舊專案、教學文章、第三方套件與 OpenAI-compatible API,現在仍然使用 Chat Completions 的寫法。因此,Chat Completions API 不需要立刻被淘汰,也不代表既有功能一定要馬上重寫。

不過,如果你現在要從零開始建立新的 OpenAI API 功能,我會建議優先理解 Responses API。

原因是 Responses API 更像是 OpenAI API 新一代的統一入口。它不只處理一般文字對話,也把結構化輸出、工具呼叫、多模態輸入、串流回應與狀態管理等能力,放在同一個 response workflow 裡。

Chat Completions API 的核心心智模型是:

messages -> assistant message

也就是你把一組 messages 傳給模型,模型回傳下一則 assistant 回覆。

Responses API 的核心心智模型則更接近:

input -> response output

也就是你提供一組 input,模型產生一個完整的 response。這個 response 可能是文字、結構化資料、工具呼叫結果、多模態輸出,或多個 output items 的組合。

所以,這篇文章不是要說 Chat Completions API 不能用了,而是要釐清:

新專案為什麼應該開始優先看 Responses API?
既有 Chat Completions 專案又該在什麼情況下保留或遷移?

我的建議是:

  • 既有 Chat Completions 功能如果穩定運作,可以先保留。
  • 新功能、新工具整合、新多模態流程,可以優先從 Responses API 開始。
  • 如果要遷移,不要一次全部重寫,先從摘要、分類、資料抽取等低風險功能開始。
  • 涉及 Tool Calling、Streaming、多輪對話或正式產品 log 的功能,應該先評估成本、測試與維護風險。

這篇不會像 Chat Completions 系列那樣寫成完整參數大全。
它的目標是建立 Responses API 的基本心智模型,並整理它和 Chat Completions API 的差異。

接下來會依序介紹:

  • Responses API 是什麼
  • 最小可用範例怎麼寫
  • input 和 messages 的差異
  • 如何讀取 response output
  • tools、structured output、streaming、多模態在 Responses API 裡怎麼理解
  • 新專案與舊專案應該如何選擇

簡單來說:

Chat Completions API 適合用來理解模型如何根據 messages 產生回答。
Responses API 則更適合用來理解新專案如何把輸入、輸出、工具、多模態與狀態管理整合成一個 response 流程。

B. Responses API 是什麼?

Responses API 是 OpenAI 目前用來建立 model response 的主要介面之一。

它的 endpoint 是:

POST /v1/responses

如果使用 Python SDK,通常會透過:

client.responses.create(...)

來建立一個 response。

最簡單的形式會像這樣:

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-5.5",
    input="請用一句話解釋 Responses API 是什麼。"
)

print(response.output_text)

這段程式的核心只有兩個部分:

  • model:指定要使用哪一個模型。
  • input:提供模型這次要處理的輸入內容。

模型處理完 input 後,會回傳一個 response object。
如果只是一般文字輸出,最方便的讀取方式通常是:

response.output_text

可以先把 Responses API 理解成:

你提供 input,模型產生 response。

它和 Chat Completions API 最大的不同,不只是參數名稱從 messages 變成 input,而是整體設計更像一個統一的 response workflow。

在 Responses API 裡,同一個 response 流程可以涵蓋:

  • 文字輸入與文字輸出
  • 圖片、檔案等多模態輸入
  • JSON 或 JSON Schema 結構化輸出
  • 自訂工具呼叫
  • 內建工具,例如 web search、file search
  • streaming 串流回應
  • conversation state 與多輪對話相關能力

也就是說,Responses API 不只是「新版聊天 API」。
它更像是把模型輸入、模型輸出、工具使用、多模態內容與狀態管理整合在一起的新介面。

如果用簡單表格來看:

面向Responses API 的角色
文字生成根據 input 產生文字回答
結構化輸出產生 JSON 或符合 schema 的資料
工具呼叫讓模型在需要時呼叫工具
多模態接收文字、圖片、檔案等輸入
串流讓回應分段傳回
狀態管理支援 response / conversation 相關工作流

因此,當你只是做一個最簡單的聊天功能時,Responses API 可以很簡單;
但當你需要 tools、structured output、file search、web search、streaming 或多模態時,它也能用同一套 response 架構往外擴充。

這也是為什麼新專案值得優先理解 Responses API。

它不是單純取代 client.chat.completions.create(...) 的另一個函式名稱,而是把 OpenAI API 的使用方式從「產生下一則 assistant message」推向「建立一個完整 response workflow」。

C. 最小可用範例:建立第一個 response

在理解 Responses API 的定位後,我們先從最小可用範例開始。

如果你只是要讓模型根據一段文字產生回答,Responses API 的寫法非常簡單:

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-5.5",
    input="請用一句話解釋 Responses API 是什麼。"
)

print(response.output_text)

這段程式做了幾件事。

首先,建立 OpenAI client:

client = OpenAI()

預設情況下,SDK 會從環境變數 OPENAI_API_KEY 讀取 API key。
因此實務上不建議把 API key 直接寫在程式碼裡。

如果尚未設定,可以先在 shell 中設定:

export OPENAI_API_KEY="你的 API key"

Windows PowerShell 則可以使用:

$env:OPENAI_API_KEY="你的 API key"

接著,使用:

client.responses.create(...)

建立一個 response。

這個最小範例中只用了兩個核心參數:

參數說明
model指定要使用哪一個模型
input提供模型要處理的輸入內容

例如:

response = client.responses.create(
    model="gpt-5.5",
    input="請用一句話解釋 Responses API 是什麼。"
)

這裡的 input 是一段純文字。
模型會根據這段文字產生 response。

如果只是一般文字輸出,最方便的讀取方式是:

response.output_text

所以:

print(response.output_text)

可能會輸出:

Responses API 是 OpenAI 用來接收輸入並產生模型回應的統一介面。

這就是一個最基本的 Responses API 請求。

C.1 和 Chat Completions 最小範例的差異

如果你熟悉 Chat Completions API,可能會記得它的最小範例通常長這樣:

completion = client.chat.completions.create(
    model="gpt-5.5",
    messages=[
        {
            "role": "user",
            "content": "請用一句話解釋 Chat Completions API 是什麼。"
        }
    ],
)

print(completion.choices[0].message.content)

兩者最大的差異是:

Chat Completions APIResponses API
client.chat.completions.create(...)client.responses.create(...)
messages=[...]input=...
completion.choices[0].message.contentresponse.output_text

也就是說,Chat Completions API 是圍繞 messages 設計;
Responses API 則是圍繞 input 和 response 設計。

如果只是最簡單的文字問答,Responses API 的寫法會更直接。

C.2 加入開發者指令

在 Chat Completions API 裡,我們常用 developer message 來設定模型行為。

在 Responses API 裡,也可以用結構化 input 表達不同角色的訊息。

例如:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "developer",
            "content": "你是一位熟悉 API 設計的技術寫作者,請使用繁體中文回答。"
        },
        {
            "role": "user",
            "content": "請用一句話解釋 Responses API 是什麼。"
        }
    ],
)

print(response.output_text)

這裡的意思是:

  • developer:應用程式希望模型遵守的規則。
  • user:使用者實際提出的問題。

這種寫法和 Chat Completions 的 messages 很接近,因此如果你已經熟悉 Chat Completions API,會很容易理解。

不過要注意,Responses API 的 input 不只可以模擬對話訊息,也可以承載更廣泛的輸入格式。後面會再介紹文字、圖片、檔案與多輪對話等不同 input 寫法。

C.3 什麼時候只用簡單字串 input?

如果你的任務很單純,例如:

  • 單輪問答
  • 一次性摘要
  • 簡單改寫
  • 翻譯
  • 產生短文案
  • 產生文章標題

可以直接使用字串形式的 input

例如:

response = client.responses.create(
    model="gpt-5.5",
    input="請幫我把這句話改寫得更適合放在產品首頁:旅行資料不該散落在聊天、相簿和收據裡。"
)

print(response.output_text)

這種寫法簡單、直覺,也很適合快速測試。

但如果你需要更清楚地區分開發者指令、使用者輸入、歷史對話、工具結果或多模態內容,就應該使用結構化 input,而不是只放一段字串。

C.4 最小範例小結

Responses API 的最小使用方式可以先記住這三個重點:

response = client.responses.create(
    model="gpt-5.5",
    input="你的輸入內容"
)

print(response.output_text)

其中:

  • client.responses.create(...) 用來建立 response。
  • model 指定使用哪個模型。
  • input 是模型要處理的輸入。
  • response.output_text 是讀取文字輸出的簡便方式。

如果你只是要做單輪文字任務,這個範例就足夠開始。

接下來,我們會進一步看 Responses API 和 Chat Completions API 在心智模型上的差異:從 messages 到 input,到底代表什麼改變。

D. 從 messages 到 input:心智模型的轉換

如果你熟悉 Chat Completions API,最需要調整的地方不是函式名稱,而是心智模型。

Chat Completions API 的核心是 messages

你會把一組對話訊息傳給模型:

completion = client.chat.completions.create(
    model="gpt-5.5",
    messages=[
        {
            "role": "developer",
            "content": "請使用繁體中文回答。"
        },
        {
            "role": "user",
            "content": "請解釋 Chat Completions API。"
        }
    ],
)

然後模型回傳下一則 assistant message:

completion.choices[0].message.content

所以 Chat Completions API 可以理解成:

messages -> assistant message

Responses API 的核心則是 input

最簡單的情況下,input 可以只是一段字串:

response = client.responses.create(
    model="gpt-5.5",
    input="請解釋 Responses API。"
)

然後你讀取:

response.output_text

所以 Responses API 可以先理解成:

input -> response output

這個差異看起來只是把 messages 換成 input,但實際上代表 API 設計重心不同。

D.1 Chat Completions 是對話訊息導向

Chat Completions API 的設計很接近聊天紀錄。

你傳入的 messages 通常會長這樣:

[
  {
    "role": "developer",
    "content": "請使用繁體中文回答。"
  },
  {
    "role": "user",
    "content": "什麼是 Chat Completions API?"
  },
  {
    "role": "assistant",
    "content": "Chat Completions API 是一個讓開發者傳入對話訊息並取得模型回覆的 API。"
  },
  {
    "role": "user",
    "content": "那它和 Responses API 有什麼不同?"
  }
]

這種結構的重點是:

  • 每一則訊息都有 role
  • 對話順序很重要
  • 模型根據目前 messages 產生下一則 assistant message
  • 多輪對話通常要由應用程式自行保存歷史 messages

因此,Chat Completions API 很適合用來理解「聊天模型如何根據上下文回答」。

D.2 Responses API 是 response workflow 導向

Responses API 雖然也可以處理對話,但它的抽象層級更高。

它不只是「給我下一則 assistant message」,而是「根據這次 input 建立一個 response」。

這個 response 可能包含:

  • 一段文字回答
  • 結構化輸出
  • 工具呼叫
  • 多個 output items
  • 圖片、檔案等輸入的處理結果
  • streaming 事件
  • 和 conversation state 相關的資訊

所以 Responses API 更適合理解成:

input -> model processing -> response output

其中 input 不一定只是聊天訊息,也可以是:

  • 一段純文字
  • 一組對話 input items
  • 圖片加文字
  • 檔案加指令
  • 工具結果
  • 多輪對話中的延續輸入

因此,Responses API 比較像一個統一的模型工作流入口,而不是單純的聊天補完介面。

D.3 input 可以簡單,也可以結構化

Responses API 的 input 可以很簡單:

response = client.responses.create(
    model="gpt-5.5",
    input="請幫我摘要這段文字。"
)

這種寫法適合單輪任務,例如:

  • 摘要
  • 翻譯
  • 改寫
  • 標題生成
  • 簡單問答

但當你需要更清楚地表達角色或上下文時,也可以使用結構化 input:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "developer",
            "content": "你是一位技術寫作者,請使用繁體中文回答。"
        },
        {
            "role": "user",
            "content": "請解釋 Responses API。"
        }
    ],
)

這種寫法和 Chat Completions 的 messages 很像,適合從舊專案概念平滑過渡。

但 Responses API 的 input 不只限於這種文字對話結構。後面如果加入圖片、檔案、工具或狀態管理,input 的角色會更明顯。

D.4 output 也不只是 message.content

在 Chat Completions API 裡,最常讀的欄位是:

completion.choices[0].message.content

這代表第一個候選回答中的 assistant 文字內容。

Responses API 裡,最簡單的文字讀取方式是:

response.output_text

但要注意,response.output_text 是方便讀取文字輸出的 shortcut。
完整 response object 裡可能還有更細的 output items,例如文字、工具呼叫、工具結果或其他型態的輸出。

所以可以這樣理解:

API常見讀取方式心智模型
Chat Completionschoices[0].message.content讀取 assistant message
Responsesresponse.output_text讀取 response 中的文字輸出

如果只是文字回答,response.output_text 很方便。
但如果你使用 tools、structured output 或 streaming,就需要理解 response output 的完整結構,而不能只看 output_text

D.5 不要把 Responses API 只當成 Chat Completions 改名

最容易誤解的地方是:把 Responses API 當成 Chat Completions API 的改名版本。

例如只記成:

messages 改成 input
choices[0].message.content 改成 output_text

這樣雖然可以幫你寫出最小範例,但不足以理解 Responses API 的設計。

更準確的理解應該是:

Chat Completions APIResponses API
對話訊息導向response workflow 導向
產生下一則 assistant message建立完整 model response
核心是 messages核心是 input
輸出是 choices 裡的 message輸出是 response object 裡的 output
適合舊聊天架構與相容性適合新專案與多工具、多模態 workflow

如果你的專案只是單純聊天,兩者看起來差異不大。
但如果你需要 structured output、tools、file search、web search、streaming、多模態或更完整的狀態管理,Responses API 的設計會更適合新功能擴充。

D.6 心智模型小結

簡單整理:

Chat Completions API:

messages -> assistant message

Responses API:

input -> response output

Chat Completions API 的重點是對話訊息。
Responses API 的重點是建立一個完整 response。

所以從 Chat Completions 轉向 Responses API 時,不只是把函式名稱從:

client.chat.completions.create(...)

改成:

client.responses.create(...)

而是要把思考方式從「下一則 assistant message」轉成「這次 input 對應的完整 response workflow」。

下一章會用表格整理 Responses API 和 Chat Completions API 的差異,幫助你更快判斷兩者在實務上該怎麼選。

E. Responses API 和 Chat Completions API 的差異

前面已經提到,Responses API 不是單純把 Chat Completions API 換一個名字。

兩者都可以讓模型根據輸入產生回答,也都可以用來做聊天、摘要、分類、資料抽取或工具呼叫。
但它們的設計重心不同。

Chat Completions API 比較像是「對話補完」介面。
Responses API 則比較像是「建立 model response 的統一工作流」介面。

可以先用這張表理解:

面向Chat Completions APIResponses API
端點/v1/chat/completions/v1/responses
SDK 寫法client.chat.completions.create(...)client.responses.create(...)
核心輸入messagesinput
基本輸出assistant messageresponse output
最常讀取completion.choices[0].message.contentresponse.output_text
心智模型messages -> assistant messageinput -> response output
工具呼叫tools / tool_choicetools workflow
多模態content partsinput items / content parts
串流chat.completion.chunkresponse streaming events
適合情境舊專案、相容性、既有聊天架構新專案、多工具、多模態、agent-like workflow

這張表不是要說 Chat Completions API 已經不能用,而是幫你判斷兩者的設計方向。

E.1 端點不同

Chat Completions API 使用:

POST /v1/chat/completions

Responses API 使用:

POST /v1/responses

如果使用 Python SDK,Chat Completions API 通常寫成:

completion = client.chat.completions.create(
    model="gpt-5.5",
    messages=[
        {
            "role": "user",
            "content": "請解釋 Chat Completions API。"
        }
    ],
)

Responses API 則寫成:

response = client.responses.create(
    model="gpt-5.5",
    input="請解釋 Responses API。"
)

最表層的差異就是:

client.chat.completions.create(...)

變成:

client.responses.create(...)

但真正重要的差異在於後面的資料模型。

E.2 核心輸入不同:messages vs input

Chat Completions API 的核心輸入是:

messages=[
    {
        "role": "user",
        "content": "請解釋 Chat Completions API。"
    }
]

Responses API 的核心輸入是:

input="請解釋 Responses API。"

或使用結構化 input:

input=[
    {
        "role": "developer",
        "content": "請使用繁體中文回答。"
    },
    {
        "role": "user",
        "content": "請解釋 Responses API。"
    }
]

可以這樣理解:

API核心輸入適合理解方式
Chat Completionsmessages一組對話訊息
Responsesinput一次 response workflow 的輸入

Chat Completions 的 messages 很明確地圍繞對話角色設計。
Responses 的 input 則更廣,可以承載單純文字、對話式 input、多模態內容或工具相關流程。

E.3 輸出讀取方式不同

Chat Completions API 最常讀取:

completion.choices[0].message.content

Responses API 最簡單讀取:

response.output_text

對一般文字任務來說,response.output_text 很直覺。

例如:

response = client.responses.create(
    model="gpt-5.5",
    input="請用一句話介紹 Responses API。"
)

print(response.output_text)

但要注意,response.output_text 是方便讀取文字輸出的 shortcut。
如果 response 裡包含工具呼叫、結構化輸出、串流事件或其他 output items,就需要理解完整 response object,而不是只看 output_text

簡單來說:

情境讀取方式
單純文字輸出response.output_text
需要看完整輸出結構response.output
Tool Calling檢查 output items 中的 tool call
Streaming讀取 streaming events
Structured Outputs依 schema parse 輸出內容

E.4 工具與內建能力的整合方向不同

Chat Completions API 也支援 tools,例如:

completion = client.chat.completions.create(
    model="gpt-5.5",
    messages=messages,
    tools=tools,
    tool_choice="auto",
)

Responses API 也支援 tools,但它的定位更像是整合在 response workflow 裡。

也就是說,Responses API 不只可以接自訂 function calling,也更自然地和內建工具搭配,例如:

  • web search
  • file search
  • code interpreter 類工具
  • 其他內建或外部工具能力

這是 Responses API 對新專案比較重要的地方:
它不只是讓模型「產生文字」,而是讓模型在建立 response 的過程中可以使用工具、檢索資料或處理多模態輸入。

當你只是做一個純文字聊天功能時,兩者差異可能不大。
但當你要做 agent-like workflow、資料查詢、文件檢索或多工具流程時,Responses API 的設計會更適合擴充。

E.5 串流模型不同

Chat Completions API 的 streaming 回傳是:

chat.completion.chunk

你通常會讀:

chunk.choices[0].delta.content

Responses API 的 streaming 則是事件導向。
它會回傳不同類型的 streaming events,而不只是單一 chat.completion.chunk 結構。

因此,如果你從 Chat Completions 遷移到 Responses API,streaming 是需要特別注意的地方。

Chat Completions streaming 的概念是:

一段一段累積 delta.content

Responses API streaming 的概念更接近:

根據不同 event type 處理 response 生成過程

所以簡單文字 streaming 仍然可以做到,但如果你有 tool calls、structured output 或多模態流程,事件處理會比 Chat Completions 的 chunk 更需要設計。

這也是為什麼遷移時不建議一開始就改最複雜的 streaming UI。
比較好的做法是先從非串流功能開始,再處理 streaming。

E.6 多模態與檔案流程更自然

Chat Completions API 也可以透過 content parts 處理圖片、音訊或檔案。
但 Responses API 的設計更偏向把多模態輸入放進統一的 input workflow 中。

例如,你可以把 Responses API 想成更適合處理這類任務:

  • 使用者輸入一段文字
  • 附上一張收據圖片
  • 再附上一份 PDF
  • 模型根據這些 input 產生摘要或結構化結果
  • 必要時呼叫 file search 或其他工具

如果你的產品需要處理圖片、文件、工具與結構化輸出,Responses API 的整體心智模型會比 Chat Completions 更自然。

E.7 什麼時候差異不大?

不是所有任務都會明顯感受到差異。

如果你的功能只是:

  • 單輪問答
  • 簡單摘要
  • 簡單改寫
  • 翻譯
  • 一般聊天

那 Chat Completions API 和 Responses API 都可以做到。

例如這兩段在概念上很接近:

completion = client.chat.completions.create(
    model="gpt-5.5",
    messages=[
        {
            "role": "user",
            "content": "請幫我摘要這段文字。"
        }
    ],
)
response = client.responses.create(
    model="gpt-5.5",
    input="請幫我摘要這段文字。"
)

如果你的舊功能已經穩定,其實不需要為了這種簡單任務立刻遷移。

Responses API 的優勢會在以下情境更明顯:

  • 新專案
  • 多模態輸入
  • 內建工具
  • file search / web search
  • 多工具 workflow
  • structured output
  • agent-like workflow
  • 想統一未來 OpenAI API 使用方式

E.8 差異小結

整理一下:

問題建議理解
Responses API 是 Chat Completions 改名嗎?不是,它是更偏 response workflow 的新介面
Chat Completions API 還能用嗎?可以,舊專案不需要立刻重寫
新專案該看哪個?優先看 Responses API
最核心差異是什麼?messages -> assistant message vs input -> response output
單純聊天差異大嗎?不一定很大
多工具、多模態、內建工具差異大嗎?Responses API 更適合
遷移要一次全部改嗎?不建議,應分階段處理

簡單來說:

Chat Completions API 適合理解對話訊息如何產生下一則回答。
Responses API 則更適合新專案,把輸入、輸出、工具、多模態與 workflow 放在同一個 response 流程中設計。

下一章會更具體看 input,也就是 Responses API 裡最核心的輸入格式。

F. input:Responses API 的核心輸入格式

在 Responses API 裡,input 是最核心的請求參數。

如果說 Chat Completions API 的核心是 messages,那 Responses API 的核心就是 input

最簡單的寫法是一段字串:

response = client.responses.create(
    model="gpt-5.5",
    input="請用一句話解釋 Responses API 是什麼。"
)

print(response.output_text)

這種寫法適合單輪、純文字、沒有複雜上下文的任務。

例如:

  • 簡單問答
  • 摘要
  • 翻譯
  • 改寫
  • 標題生成
  • 短文案產生

但 Responses API 的 input 不只可以是一段字串。
當你的任務變複雜時,input 也可以改成更結構化的格式,用來表達角色、上下文、多模態內容或多輪對話。

F.1 字串 input:最簡單的用法

最小可用範例通常會使用字串 input。

response = client.responses.create(
    model="gpt-5.5",
    input="請幫我把這句話改寫得更適合放在產品首頁:旅行資料不該散落在聊天、相簿和收據裡。"
)

print(response.output_text)

這種寫法的優點是簡單。

你不需要思考 role、content parts 或 input items,只要把要處理的文字放進 input 即可。

適合情境:

情境適合原因
一次性摘要不需要多輪上下文
翻譯輸入清楚,輸出單純
改寫任務明確
標題生成單輪輸入即可
簡單問答不需要額外狀態

如果你的需求只是「丟一段文字,拿一段回答」,字串 input 就足夠了。

F.2 結構化 input:加入角色與指令

當你需要更清楚地區分「應用程式規則」和「使用者輸入」時,可以使用結構化 input。

例如:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "developer",
            "content": "你是一位熟悉 API 設計的技術寫作者,請使用繁體中文回答。"
        },
        {
            "role": "user",
            "content": "請用一句話解釋 Responses API 是什麼。"
        }
    ],
)

print(response.output_text)

這裡的 input 是一個陣列,每個項目代表一段輸入內容。

可以把它理解成:

  • developer:應用程式希望模型遵守的規則。
  • user:使用者這次實際提出的問題。

這種寫法和 Chat Completions API 的 messages 很接近,因此如果你已經熟悉 Chat Completions,會很好理解。

適合情境:

  • 需要固定回答語言
  • 需要指定模型角色
  • 需要限制回答格式
  • 需要加入產品規則
  • 需要區分開發者指令與使用者輸入

例如客服助理:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "developer",
            "content": "你是 TallyTrip 的客服助理。請使用繁體中文回答,並避免承諾未確認的帳務資訊。"
        },
        {
            "role": "user",
            "content": "我朋友已經付錢了,為什麼系統還顯示他欠款?"
        }
    ],
)

這樣模型就不會只根據使用者問題回答,而會同時受到產品規則約束。

F.3 input content 可以包含多個內容片段

除了純文字,input 的內容也可以使用 content parts 來表示更複雜的輸入。

例如:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "user",
            "content": [
                {
                    "type": "input_text",
                    "text": "請描述這張圖片。"
                },
                {
                    "type": "input_image",
                    "image_url": "https://example.com/photo.jpg"
                }
            ]
        }
    ],
)

print(response.output_text)

這種寫法表示同一則 user input 裡,同時包含:

  • 一段文字指令
  • 一張圖片

這是 Responses API 很重要的地方:
input 不只是文字,也可以承載多模態內容。

常見 content 類型可以先理解成:

類型用途
input_text文字輸入
input_image圖片輸入
input_file檔案輸入

實際支援哪些 content type,要以你使用的模型與官方文件為準。
不是每個模型都支援所有多模態輸入。

F.4 圖片 input:文字加圖片

如果模型支援圖片輸入,可以把文字和圖片放在同一個 input item 中。

例如:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "user",
            "content": [
                {
                    "type": "input_text",
                    "text": "請讀取這張收據,整理出店名、日期、幣別與總金額。"
                },
                {
                    "type": "input_image",
                    "image_url": "https://example.com/receipt.jpg"
                }
            ]
        }
    ],
)

print(response.output_text)

這種寫法很適合:

  • 收據圖片分析
  • 票券截圖整理
  • UI 截圖說明
  • 圖片描述
  • 商品圖片分析
  • 旅遊照片整理

以旅行工具為例,使用者可能上傳:

  • 收據照片
  • 交通票券
  • 訂位截圖
  • 景點資訊截圖

你可以讓模型先讀取圖片內容,再整理成文字或結構化資料。

不過要注意:圖片理解不等於高精度 OCR。
如果資料涉及金額、日期、訂單、付款或正式帳務,仍然應該做後端驗證與使用者確認。

F.5 檔案 input:讓模型處理文件

Responses API 也可以用來處理檔案輸入,例如文件摘要、檔案問答或資料整理。

概念上會像這樣:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "user",
            "content": [
                {
                    "type": "input_text",
                    "text": "請整理這份文件的三個重點。"
                },
                {
                    "type": "input_file",
                    "file_id": "file_abc123"
                }
            ]
        }
    ],
)

print(response.output_text)

檔案 input 適合:

  • 文件摘要
  • 報告重點整理
  • 合約初步分析
  • PDF 內容理解
  • 檔案轉結構化資料

但要區分兩種情境:

需求建議方向
單次請求附上一份文件,請模型讀取file input
要在大量文件中搜尋相關內容file search / retrieval / RAG

如果你只是要模型看這次附上的文件,file input 可以處理。
如果你要建立知識庫,讓模型在多份文件中檢索答案,就應該考慮 file search 或 RAG 流程。

F.6 多輪對話 input

在 Chat Completions API 裡,多輪對話通常是自己維護 messages

例如:

messages = [
    {"role": "developer", "content": "請使用繁體中文回答。"},
    {"role": "user", "content": "什麼是 Responses API?"},
    {"role": "assistant", "content": "Responses API 是 OpenAI 用來建立模型回應的介面。"},
    {"role": "user", "content": "那它和 Chat Completions 有什麼不同?"}
]

在 Responses API 裡,也可以用類似的 input 結構表示對話上下文:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "developer",
            "content": "請使用繁體中文回答。"
        },
        {
            "role": "user",
            "content": "什麼是 Responses API?"
        },
        {
            "role": "assistant",
            "content": "Responses API 是 OpenAI 用來建立模型回應的介面。"
        },
        {
            "role": "user",
            "content": "那它和 Chat Completions 有什麼不同?"
        }
    ],
)

這種方式適合你想手動控制上下文的情境。

不過 Responses API 也有和 response / conversation state 相關的能力,後面會再單獨介紹。
這裡先記住一點:即使 Responses API 的狀態管理能力比 Chat Completions 更完整,正式產品中仍然應該規劃自己的 session、conversation、request log 與資料保存策略。

不要把所有對話狀態管理都交給模型端。

F.7 input 設計的實務原則

設計 input 時,可以用幾個原則判斷。

一、任務簡單時,用字串 input

例如:

input="請把這段文字翻成繁體中文。"

這樣最簡單,也最容易測試。

二、需要規則時,用 developer + user 結構

例如:

input=[
    {
        "role": "developer",
        "content": "請使用繁體中文回答,並保持簡潔。"
    },
    {
        "role": "user",
        "content": user_input
    }
]

這樣可以把產品規則和使用者輸入分開。

三、有圖片、檔案時,用 content parts

例如:

input=[
    {
        "role": "user",
        "content": [
            {
                "type": "input_text",
                "text": "請整理這張收據。"
            },
            {
                "type": "input_image",
                "image_url": receipt_url
            }
        ]
    }
]

這樣比把圖片描述塞進文字更自然。

四、多輪對話要有明確上下文策略

不要無限制把所有歷史都塞進 input。
原因和 Chat Completions 一樣:

  • token 成本會增加
  • latency 會增加
  • 過舊資訊可能干擾回答
  • 敏感資料會更容易被帶入模型請求

正式產品中通常會:

  • 保留最近幾輪
  • 摘要舊對話
  • 按 session 管理狀態
  • 過濾敏感資訊
  • 記錄 request log
  • 控制 token 長度

F.8 input 小結

整理一下,Responses API 的 input 可以有不同層次:

input 形式適合情境
字串單輪文字任務
developer + user 結構需要規則與使用者輸入分離
content parts圖片、檔案、多模態輸入
多輪 input需要對話上下文
搭配 state / conversation較完整的多輪流程

最小情境下,你只需要:

response = client.responses.create(
    model="gpt-5.5",
    input="你的輸入內容"
)

但當產品需求變複雜時,input 就會從單純字串,逐步變成承載文字、角色、圖片、檔案、工具與狀態的核心資料結構。

下一章會看 output,也就是 Responses API 回傳後,應該如何讀取模型產生的結果。

G. output:如何讀取模型回覆

前一章介紹了 Responses API 的核心輸入格式 input

接下來要看的是輸出,也就是模型產生 response 之後,我們要怎麼讀取結果。

在最簡單的文字任務中,你通常只需要讀:

response.output_text

例如:

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-5.5",
    input="請用一句話解釋 Responses API 是什麼。"
)

print(response.output_text)

這是 Responses API 最方便的文字輸出讀取方式。

如果模型回傳的是一般文字,response.output_text 就足以涵蓋大多數入門情境。

G.1 response.output_text:最簡單的文字讀取方式

response.output_text 可以理解成一個方便用的 shortcut。

它會幫你從 response object 裡取出模型產生的文字內容。

例如:

response = client.responses.create(
    model="gpt-5.5",
    input="請幫我寫一句 TallyTrip 的產品標語。"
)

print(response.output_text)

可能輸出:

讓旅程資料、收據與分帳一次整理清楚。

如果你的功能是:

  • 一般問答
  • 摘要
  • 翻譯
  • 改寫
  • 文章草稿
  • 標題生成
  • 簡單聊天

通常先使用 response.output_text 就可以。

這也是 Responses API 對入門者比較友善的地方:不用像 Chat Completions API 一樣每次都寫:

completion.choices[0].message.content

而是可以直接讀:

response.output_text

G.2 response object 不只包含文字

不過,response.output_text 只適合「單純文字輸出」的情境。

Responses API 回傳的 response object 其實可能包含更多資訊,例如:

  • output items
  • 文字內容
  • tool calls
  • structured output
  • refusal 或安全相關資訊
  • usage
  • metadata
  • streaming 事件中累積的輸出

所以如果你使用的是比較進階的功能,例如:

  • Tool Calling
  • Structured Outputs
  • Streaming
  • 多模態輸入
  • 內建工具
  • file search
  • web search

就不能只把 response 理解成一段文字。

可以先用這個方式區分:

情境常見讀取方式
單純文字回答response.output_text
需要完整輸出結構response.output
Tool Calling檢查 output items 裡的 tool call
Structured Outputs解析模型輸出的 JSON / schema 結果
Streaming根據 event type 處理輸出
成本分析查看 usage

因此,入門時記住 response.output_text 就好;
但做正式產品時,應該理解完整 response object 的結構。

G.3 response.output:完整輸出項目

如果要看更完整的模型輸出,可以查看:

response.output

概念上,response.output 會包含一組 output items。

這些 output items 可能包含文字,也可能包含工具呼叫或其他輸出項目。

簡化概念可能像這樣:

[
  {
    "type": "message",
    "role": "assistant",
    "content": [
      {
        "type": "output_text",
        "text": "Responses API 是 OpenAI 用來建立模型回應的介面。"
      }
    ]
  }
]

如果只是一般文字回答,response.output_text 會比你手動解析 response.output 更方便。

但如果你需要知道模型到底產生了哪些 output items,就應該看 response.output

例如:

for item in response.output:
    print(item.type)
    print(item)

這在除錯 tool calling、structured output 或多模態流程時很有用。

G.4 文字輸出和 output items 的關係

可以這樣理解:

response.output 是完整輸出結構
response.output_text 是從完整輸出中整理出來的文字

也就是說:

  • response.output:比較完整,但需要自己解析。
  • response.output_text:比較方便,但只適合取文字。

對入門者來說,先使用:

response.output_text

對正式產品來說,尤其當你使用工具、結構化輸出或 streaming 時,應該逐步理解:

response.output

G.5 和 Chat Completions 的讀取方式比較

如果用 Chat Completions API,最常見的讀法是:

completion.choices[0].message.content

而 Responses API 最常見的文字讀法是:

response.output_text

可以用這張表對照:

API最簡單文字讀取方式
Chat Completions APIcompletion.choices[0].message.content
Responses APIresponse.output_text

Chat Completions 的 response 結構比較像:

choices -> message -> content

Responses API 的 response 結構則比較像:

response -> output -> output items

但 Responses API 額外提供 output_text,讓純文字任務不用每次都自己走完整 output 結構。

G.6 usage:不要忘記用量資訊

除了模型輸出文字,正式產品也應該關心 token usage。

在 response object 裡,通常可以查看 usage 相關資訊:

print(response.usage)

usage 可以用來分析:

  • input tokens
  • output tokens
  • total tokens
  • 成本
  • 哪個功能最耗 token
  • 哪個 prompt 太長
  • 哪個模型成本效益較好

例如:

response = client.responses.create(
    model="gpt-5.5",
    input="請摘要這段文章。"
)

print(response.output_text)
print(response.usage)

如果只是 demo,可能不需要特別記錄 usage。
但如果是正式產品,建議至少把 usage、model、feature、user id、latency 一起寫入 log。

這樣後續才能做成本分析與除錯。

G.7 輸出讀取的實務建議

我會建議用這個方式判斷該讀哪個欄位:

需求建議
只要顯示文字給使用者使用 response.output_text
要除錯完整模型輸出查看 response.output
要處理工具呼叫檢查 output items
要處理 JSON / schema解析結構化輸出
要做 streaming根據 streaming event 處理
要做成本分析記錄 response.usage

也就是說:

  • 入門:用 response.output_text
  • 進階:看 response.output
  • 正式產品:再加上 usage、log、錯誤處理與 validation

G.8 output 小結

整理一下,Responses API 的輸出可以先分成兩層:

欄位用途
response.output_text快速讀取文字輸出
response.output查看完整 output items

如果只是簡單文字任務:

print(response.output_text)

就足夠了。

如果你開始使用 tools、structured output、streaming 或多模態功能,就要進一步理解完整 response object。

Responses API 的重點不是只回傳一段文字,而是建立一個 response。
這個 response 可以包含文字、工具呼叫、結構化資料與其他 output items。

下一章會介紹 Structured Outputs,也就是如何讓 Responses API 回傳更適合程式解析的結構化資料。

H. Structured Outputs:Responses API 裡的結構化輸出

很多時候,我們不只是想讓模型回覆一段文字,而是希望它回傳可以被程式穩定解析的資料。

例如:

  • 從使用者訊息中抽取姓名、日期、金額
  • 將客服訊息分類成固定類別
  • 把文章整理成標題、摘要、關鍵字
  • 從收據文字中整理店名、日期、幣別與總金額
  • 讓模型輸出前端 UI 可以直接使用的資料結構

這類需求就適合使用 Structured Outputs。

Responses API 支援透過 text.format 指定輸出格式,其中可以使用 JSON Schema 讓模型依照指定結構輸出資料。OpenAI 文件也把 Structured output 列為核心概念之一,並在 Responses API 的 create response 文件中提供 text / format 相關設定。(OpenAI 平台Attachment.tiff)

H.1 什麼時候需要 Structured Outputs?

如果模型輸出是給人看的,例如聊天回答、文章草稿、翻譯、摘要,通常直接用文字就可以。

例如:

response = client.responses.create(
    model="gpt-5.5",
    input="請用一句話介紹 TallyTrip。"
)

print(response.output_text)

這種輸出很適合直接顯示給使用者。

但如果模型輸出要交給程式處理,就不應該只依賴自然語言。

例如你希望模型回傳:

{
  "name": "TallyTrip",
  "summary": "一個協助旅伴整理旅程資料、收據與分帳的工具。",
  "features": ["收據 OCR", "多人分帳", "多幣別結算"]
}

這時候就應該用 JSON Schema 明確定義輸出結構,而不是只在 prompt 裡寫「請用 JSON 回答」。

H.2 不要只靠 prompt 要求 JSON

很多人一開始會這樣寫:

response = client.responses.create(
    model="gpt-5.5",
    input="請把 TallyTrip 整理成 JSON,包含 name、summary、features。"
)

模型可能真的會輸出 JSON,但也可能在前後加上說明文字,例如:

以下是整理後的 JSON:

{
  "name": "TallyTrip",
  "summary": "一個旅行協作與分帳工具。",
  "features": ["收據 OCR", "多人分帳"]
}

這對人類來說沒問題,但對程式來說,前面的「以下是整理後的 JSON:」可能會讓 JSON parser 失敗。

所以,如果你的後端要穩定解析模型輸出,應該使用 Structured Outputs,而不是只靠 prompt 約束格式。

H.3 使用 JSON Schema 指定輸出格式

在 Responses API 裡,可以用 text.format 指定模型輸出要符合某個 JSON Schema。

概念範例如下:

response = client.responses.create(
    model="gpt-5.5",
    input="TallyTrip 是一個給自由行小團體使用的旅程協作工具,支援收據 OCR、多人分帳與多幣別結算。",
    text={
        "format": {
            "type": "json_schema",
            "name": "product_summary",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {
                        "type": "string"
                    },
                    "summary": {
                        "type": "string"
                    },
                    "features": {
                        "type": "array",
                        "items": {
                            "type": "string"
                        }
                    }
                },
                "required": [
                    "name",
                    "summary",
                    "features"
                ],
                "additionalProperties": False
            }
        }
    },
)

print(response.output_text)

可能輸出:

{
  "name": "TallyTrip",
  "summary": "TallyTrip 是一個給自由行小團體使用的旅程協作工具。",
  "features": [
    "收據 OCR",
    "多人分帳",
    "多幣別結算",
    "旅程協作"
  ]
}

這樣後端就可以把結果 parse 成 JSON,再寫入資料庫、傳給前端或接到下一個流程。

H.4 strict:要求模型更嚴格遵守 schema

在 JSON Schema 設定中,可以使用:

"strict": true

它的目的是讓模型更嚴格地遵守你提供的 schema。

例如你定義:

{
  "type": "object",
  "properties": {
    "category": {
      "type": "string",
      "enum": ["billing", "account", "technical", "general"]
    },
    "priority": {
      "type": "string",
      "enum": ["low", "medium", "high"]
    }
  },
  "required": ["category", "priority"],
  "additionalProperties": false
}

模型就應該只輸出你允許的欄位與值。

這很適合分類任務,例如客服訊息分類:

response = client.responses.create(
    model="gpt-5.5",
    input="我昨天付款了,但是帳號還是沒有升級。",
    text={
        "format": {
            "type": "json_schema",
            "name": "support_ticket_category",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "category": {
                        "type": "string",
                        "enum": ["billing", "account", "technical", "general"]
                    },
                    "priority": {
                        "type": "string",
                        "enum": ["low", "medium", "high"]
                    },
                    "reason": {
                        "type": "string"
                    }
                },
                "required": ["category", "priority", "reason"],
                "additionalProperties": False
            }
        }
    },
)

print(response.output_text)

可能輸出:

{
  "category": "billing",
  "priority": "medium",
  "reason": "使用者已付款但帳號尚未升級,屬於付款或訂閱狀態問題。"
}

H.5 適合使用 Structured Outputs 的情境

Structured Outputs 很適合用在「模型輸出要交給程式處理」的場景。

情境說明
資料抽取從文字中抽取欄位
客服分類將訊息分類成固定類別
收據整理抽取店名、日期、金額、幣別
表單填寫將自然語言轉成表單欄位
文章摘要輸出 title、summary、keywords
前端 UI回傳可直接渲染的資料結構
後端流程讓模型輸出可以接到下一步 API

以 TallyTrip 來說,可以用在:

  • 收據 OCR 後整理成花費草稿
  • 票券截圖整理成行程草稿
  • 使用者輸入轉成旅程任務
  • 將旅伴聊天內容整理成待辦事項
  • 將分帳問題分類成帳務、成員、幣別或收據問題

H.6 Structured Outputs 不等於內容一定正確

Structured Outputs 可以提高格式穩定性,但它不能保證內容一定正確。

例如模型可能輸出符合 schema 的 JSON:

{
  "merchant": "Tokyo Coffee",
  "date": "2026-05-20",
  "currency": "JPY",
  "total": 1400
}

這個格式可能是對的,但 total 是否真的正確,仍然要看 OCR、圖片品質、輸入資料與模型判斷。

因此正式產品中仍然需要:

  • 後端 schema validation
  • 欄位合理性檢查
  • 金額與幣別驗證
  • 使用者確認
  • 錯誤與 fallback 流程

特別是收據、付款、帳務、法律、醫療等高精確度場景,不應該把模型輸出直接當成最終事實。

H.7 Responses API 和 Chat Completions 的差異

Chat Completions API 也支援 Structured Outputs。
差別不在於「誰能不能做」,而在於 Responses API 把 structured output 放進更完整的 response workflow 裡。

可以這樣理解:

面向Chat Completions APIResponses API
結構化輸出response_formattext.format
核心輸入messagesinput
核心輸出assistant messageresponse output
適合情境舊專案、既有聊天流程新專案、工具與多模態 workflow
搭配工具可搭配 tools更自然整合在 response workflow

如果你已經在 Chat Completions 裡用 response_format 做得很穩,不一定要立刻遷移。
但如果你是新功能,尤其還會搭配 tools、file search、多模態或狀態管理,就可以優先考慮 Responses API。

H.8 Structured Outputs 小結

整理一下:

  • 如果輸出給人看,文字輸出通常就夠。
  • 如果輸出給程式處理,建議使用 Structured Outputs。
  • 不要只靠 prompt 要求模型輸出 JSON。
  • 使用 JSON Schema 可以讓模型輸出更穩定。
  • strict: true 可以要求模型更嚴格遵守 schema。
  • 正式產品仍然要做 validation 和錯誤處理。
  • Structured Outputs 控制的是格式,不保證內容一定正確。

簡單來說:

Structured Outputs 讓 Responses API 不只是產生文字,而是能產生可被程式使用的資料。

下一章會介紹 tools,也就是如何讓 Responses API 在產生 response 的過程中使用內建工具或呼叫你自己的後端函式。

I. tools:內建工具與自訂工具

Responses API 的另一個重點是 tools

如果模型只需要根據輸入內容產生文字,通常不需要工具。
但如果模型需要查詢資料、呼叫後端邏輯、搜尋文件或取得即時資訊,就會用到 tools。

可以先用一句話理解:

tools 讓模型在建立 response 的過程中,可以使用外部能力,而不是只靠模型本身回答。

Responses API 的 tools 可以涵蓋幾種方向:

  • 自訂工具,也就是 function calling
  • OpenAI 內建工具,例如 web search、file search、code interpreter 等
  • MCP tools,用來連接外部系統或第三方工具

官方 API Reference 也把 tools、web search、file search、code interpreter、MCP 等放在 Responses API 相關的工具工作流中。OpenAI 平台

I.1 為什麼需要 tools?

模型本身很擅長理解文字、整理內容、產生回答,但它不會自動知道你系統裡的即時資料。

例如使用者問:

這趟東京旅行目前總共花了多少日圓?

模型如果沒有工具,只能根據 prompt 裡已有的資料回答。
但如果你的後端有旅程花費資料庫,就可以提供一個工具讓模型查詢。

工具可以用在:

情境工具用途
查訂單查詢訂單狀態
查旅程查詢旅程資訊
查花費查詢分帳與付款資料
查文件從知識庫或檔案中找答案
查網路取得較新的公開資訊
執行計算進行特定計算或格式轉換
建立資料建立行程、任務或草稿

這讓模型不只是「生成文字」,而是可以接上產品裡真實存在的資料與流程。

I.2 自訂工具:function calling

最常見的工具是自訂 function calling。

你可以定義一個工具,告訴模型:

  • 工具名稱是什麼
  • 工具用途是什麼
  • 工具需要哪些參數
  • 參數型別與限制是什麼

例如查詢旅程花費的工具:

tools = [
    {
        "type": "function",
        "name": "get_trip_expense_summary",
        "description": "查詢指定旅程的花費總結,包含總金額、幣別與每位成員的分攤資訊。",
        "parameters": {
            "type": "object",
            "properties": {
                "trip_id": {
                    "type": "string",
                    "description": "旅程 ID。"
                },
                "currency": {
                    "type": "string",
                    "enum": ["TWD", "JPY", "USD", "KRW", "EUR"]
                }
            },
            "required": ["trip_id", "currency"],
            "additionalProperties": False
        }
    }
]

接著在 Responses API 請求中提供 tools:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "developer",
            "content": "你是 TallyTrip 的旅程助理。需要查詢旅程花費時,請使用工具。"
        },
        {
            "role": "user",
            "content": "這趟東京旅行目前總共花了多少日圓?trip_id=tokyo_2026"
        }
    ],
    tools=tools,
)

模型如果判斷需要查詢資料,就可能在 response output 中產生工具呼叫。
你的程式再根據工具名稱與參數,去執行真正的後端函式。

重點是:

模型只會提出工具呼叫意圖。
真正執行工具的是你的程式。

I.3 工具參數仍然要驗證

即使工具參數是模型根據 schema 產生的,也不能直接信任。

例如模型可能產生:

{
  "trip_id": "tokyo_2026",
  "currency": "JPY"
}

你的後端仍然要檢查:

  • trip_id 是否存在
  • 目前使用者是否有權限存取這趟旅程
  • currency 是否是系統支援的幣別
  • 參數是否符合 schema
  • 查詢是否會暴露不該看的資料

如果工具是寫入型操作,例如:

  • 建立花費
  • 刪除收據
  • 發送 email
  • 建立訂單
  • 付款
  • 修改帳務資料

就更需要使用者確認與權限控管。

Tool Calling 不是讓模型繞過後端邏輯。
它應該接在你原本就設計好的 API、權限系統與 validation 流程上。

I.4 內建工具:web search、file search、code interpreter

Responses API 的優勢之一,是更自然地整合 OpenAI 的內建工具。

常見內建工具包括:

工具用途
web search搜尋公開網路資訊
file search從檔案或知識庫中檢索內容
code interpreter執行程式、分析資料或處理檔案

這些工具讓模型不只依賴訓練資料,而是可以在 response 流程中取得外部資訊或執行特定任務。

例如:

  • 使用 web search 查最新公開資訊
  • 使用 file search 從公司文件或產品文件中找答案
  • 使用 code interpreter 分析 CSV、計算資料或產生圖表

如果你的應用需要「查資料再回答」,Responses API 的 tools workflow 會比單純文字生成更適合。

不過,也要注意:

  • web search 結果仍要評估可靠性
  • file search 需要規劃檔案上傳、權限與資料更新
  • code interpreter 適合資料處理,但不應用來執行高風險操作
  • 內建工具是否可用,取決於模型、帳號、專案與 API 支援情況

I.5 file search 適合什麼情境?

如果你的產品有一批文件,想讓模型從裡面找答案,可以考慮 file search。

例如:

  • 公司內部知識庫
  • 產品文件
  • FAQ
  • API 文件
  • 使用手冊
  • 合約或報告
  • 使用者上傳的旅程資料

以 TallyTrip 為例,file search 可能用在:

  • 從旅程文件中找訂位資訊
  • 從上傳的 PDF 中整理行程重點
  • 從使用者提供的旅行資料中回答問題
  • 從產品說明文件中回答客服問題

file search 和單次 file input 不一樣。

需求建議
這次請求附上一份文件,請模型讀它file input
在一批文件中檢索相關內容file search
建立長期知識庫file search / retrieval / RAG

如果只是一次性文件理解,file input 可能夠用。
如果要長期查找多份文件,file search 會更適合。

I.6 web search 適合什麼情境?

如果問題需要最新公開資訊,模型本身可能不知道最新狀態。
這時候可以使用 web search。

適合情境包括:

  • 查最新政策
  • 查公開新聞
  • 查產品目前價格
  • 查公開文件更新
  • 查旅遊景點營業資訊
  • 查 API 或工具最新變更

但 web search 也有風險:

  • 搜尋結果可能不完整
  • 網頁內容可能過期
  • 來源可靠性不同
  • 需要引用與來源判斷
  • 搜尋結果不一定代表官方結論

因此,如果是重要資訊,尤其是法律、醫療、金融、政策、價格、API 文件,仍然要優先依官方來源確認。

I.7 MCP tools:連接外部系統

MCP tools 可以先理解成一種讓模型連接外部工具或資料來源的方式。

如果你的公司或產品已經有很多系統,例如:

  • CRM
  • 任務管理系統
  • 文件系統
  • 內部資料庫
  • 第三方 SaaS
  • 自動化工具

MCP 可以讓這些工具以更標準化的方式接入模型工作流。

這篇先不深入 MCP 的實作細節。
你可以先把它理解成:

如果 function calling 是你自己定義單一工具,MCP 則更像是讓模型接入一組外部工具或服務的標準化方式。

對一般入門者來說,先理解 function calling 就夠了。
等你開始做多工具、多系統、多資料來源整合時,再深入研究 MCP。

I.8 tools 的實務設計原則

設計 tools 時,不要只想「讓模型能做更多事」,而要先想清楚風險與邊界。

一、工具名稱要明確

不好:

get_data

比較好:

get_trip_expense_summary

工具名稱越清楚,模型越容易判斷何時使用。

二、description 要寫清楚

不好:

取得資料

比較好:

查詢指定旅程的花費總結,包含總金額、幣別與每位成員的分攤資訊。

description 會影響模型是否正確選用工具。

三、參數 schema 要限制清楚

如果幣別只能是固定值,就用 enum:

"currency": {
  "type": "string",
  "enum": ["TWD", "JPY", "USD", "KRW", "EUR"]
}

不要讓模型自由產生不支援的值。

四、工具分風險等級

工具類型風險範例
查詢型低到中查天氣、查文件、查旅程
建立型建立草稿、建立任務
修改型中到高修改訂單、更新帳務
刪除型刪除資料、取消訂單
外部動作發信、付款、下單

高風險工具應該加上使用者確認,不要讓模型直接執行。

五、工具結果也要整理

工具回傳的資料可能很長或很複雜。
你可以讓模型根據工具結果整理成使用者看得懂的回答,但不要讓模型改寫重要數值。

例如帳務資料、金額、付款狀態,應該保留原始資料來源與後端驗證。

I.9 Responses API 和 Chat Completions tools 的差異

Chat Completions API 也有 tools / tool_choice。
所以差異不是「Chat Completions 不能呼叫工具」。

真正差異是設計方向。

面向Chat Completions APIResponses API
工具定位對話中的工具呼叫response workflow 的一部分
內建工具整合較分散更自然整合 web search、file search 等
多工具流程可以做,但較像聊天延伸更適合 agent-like workflow
新專案方向可用於舊架構建議優先理解
狀態與 output以 assistant message 為核心以 response output items 為核心

如果你已經在 Chat Completions 裡建立穩定的 tools 流程,不一定要立刻遷移。
但如果你正在做新功能,尤其需要 file search、web search、多工具流程或多模態輸入,就可以優先從 Responses API 開始。

I.10 tools 小結

整理一下:

  • tools 讓模型可以在 response 過程中使用外部能力。
  • 自訂工具通常就是 function calling。
  • 內建工具可能包含 web search、file search、code interpreter 等。
  • MCP tools 適合更標準化地連接外部系統。
  • 模型只提出工具呼叫意圖,真正執行的是你的後端。
  • 工具參數一定要驗證。
  • 高風險工具要加上權限檢查與使用者確認。
  • 新專案如果需要多工具、多模態或內建工具,Responses API 會更適合。

簡單來說:

Responses API 的 tools 讓模型不只是產生回答,而是可以在建立 response 的過程中查資料、呼叫工具,並把結果整理成使用者能理解的輸出。

下一章會介紹 Streaming,也就是如何讓 Responses API 的回應分段傳回,支援即時聊天 UI。

J. Streaming:串流回應怎麼看

如果你正在做聊天 UI,通常會希望模型不要等完整回答生成完才回傳,而是像 ChatGPT 一樣,一段一段顯示文字。

這就是 streaming。

在非串流模式下,Responses API 會等模型完成整個 response 後才回傳結果:

response = client.responses.create(
    model="gpt-5.5",
    input="請用三句話介紹 Responses API。"
)

print(response.output_text)

這種方式很適合:

  • 摘要
  • 分類
  • 資料抽取
  • 後台批次任務
  • 需要完整結果後再處理的流程

但如果是聊天介面,使用者通常不想等到整段回答完成才看到內容。
這時候就可以使用 streaming。

J.1 啟用 streaming

Responses API 可以使用 stream=True 啟用串流回應:

stream = client.responses.create(
    model="gpt-5.5",
    input="請用三句話介紹 Responses API。",
    stream=True,
)

for event in stream:
    print(event)

啟用 streaming 後,API 會回傳一連串事件,而不是一次回傳完整 response object。

這和 Chat Completions API 的 streaming 不太一樣。

Chat Completions API 串流時,你通常處理的是:

chat.completion.chunk

並讀取:

chunk.choices[0].delta.content

Responses API 串流時,則是事件導向:

response streaming events

你需要根據不同 event type 來處理文字、工具呼叫、完成狀態或錯誤。

J.2 讀取文字串流

在最簡單的文字輸出情境中,你的目標通常是把模型新增的文字片段印出來或傳給前端。

概念上會像這樣:

stream = client.responses.create(
    model="gpt-5.5",
    input="請用三句話介紹 Responses API。",
    stream=True,
)

for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="")

這段程式的重點是:

event.type == "response.output_text.delta"

代表這個 event 包含新的文字片段。

而:

event.delta

則是這次新增的文字。

你可以把每個 delta 累積起來:

parts = []

for event in stream:
    if event.type == "response.output_text.delta":
        parts.append(event.delta)
        print(event.delta, end="")

full_text = "".join(parts)

這樣就可以同時做到:

  • 即時顯示文字
  • 最後取得完整回答

J.3 Streaming 和 response.output_text 的差異

非串流模式下,你可以直接讀:

response.output_text

串流模式下,因為 response 是分段傳回,所以你不能一開始就拿到完整的 output_text

你需要自己累積文字片段:

parts = []

for event in stream:
    if event.type == "response.output_text.delta":
        parts.append(event.delta)

full_text = "".join(parts)

可以這樣理解:

模式讀取方式
非串流response.output_text
串流累積 response.output_text.delta

如果你只是做後端資料抽取,非串流比較簡單。
如果你正在做聊天 UI,串流體驗會好很多。

J.4 常見 streaming events

Responses API 的 streaming 是 event-based。
你不應該假設每個 event 都是文字。

常見 event 類型可以先這樣理解:

event type可能用途
response.createdresponse 建立
response.output_text.delta新增文字片段
response.output_text.done文字輸出完成
response.completedresponse 完成
response.failedresponse 失敗
response.in_progressresponse 進行中
tool call 相關 events工具呼叫過程
error 相關 events錯誤資訊

實際 event type 會依模型、工具、輸出格式與 SDK 版本而有所不同。
因此正式產品中,程式應該能處理未知 event type,而不是只寫死一種事件。

簡化寫法:

for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="")
    elif event.type == "response.completed":
        print("\n完成")
    elif event.type == "response.failed":
        print("\n失敗:", event)
    else:
        # 其他 event 可以先記錄,方便除錯
        pass

J.5 前端聊天 UI 怎麼使用 streaming?

在產品中,後端通常不會直接把 OpenAI 的 event 原樣丟給前端,而是轉成自己的格式。

例如後端收到:

response.output_text.delta

就轉成前端可以處理的事件:

{
  "type": "content",
  "content": "新的文字片段"
}

前端收到後,把內容追加到目前 assistant message:

let assistantMessage = "";

function onContentDelta(content) {
  assistantMessage += content;
  renderAssistantMessage(assistantMessage);
}

當後端收到完成事件時,再傳:

{
  "type": "done"
}

前端就可以把 message 狀態從:

streaming

改成:

done

正式聊天 UI 還要處理:

  • 使用者取消生成
  • 網路中斷
  • partial message
  • loading 狀態
  • 重新產生回答
  • markdown 尚未閉合
  • code block 尚未完成
  • tool call 中的等待狀態
  • error message 顯示

所以 streaming 不只是 API 參數,也會影響整個前後端狀態設計。

J.6 Streaming 搭配 tools 時會更複雜

如果你只串流文字,處理方式很單純。
但如果同時使用 tools,streaming 事件就會更複雜。

因為 response 過程中可能出現:

  • 模型開始產生文字
  • 模型決定呼叫工具
  • 工具參數分段出現
  • 後端執行工具
  • 工具結果回到模型
  • 模型根據工具結果產生最後回答

這時候你不只要處理文字 delta,也要處理工具呼叫相關 event。

實務建議是:

如果你剛開始使用 tools,先用非串流模式把流程做穩。
等工具呼叫、參數驗證、錯誤處理都完成後,再加入 streaming。

這樣會比一開始就同時處理 streaming 和 tool calling 更穩。

J.7 Streaming 不一定適合所有任務

Streaming 很適合使用者正在等待的互動流程,但不是所有任務都需要。

適合 streaming:

情境原因
聊天 UI使用者可以即時看到文字
長文生成降低等待感
即時客服體驗更接近真人回覆
使用者正在看畫面的互動回應更即時

不一定需要 streaming:

情境原因
分類任務結果很短,一次回傳即可
結構化資料抽取通常要完整 JSON 後再 parse
批次摘要使用者不在等待畫面
背景任務不需要即時顯示
tool calling 初期開發先非串流較好除錯

如果你的輸出最後要被程式解析,例如 JSON Schema 結果,通常非串流模式會更簡單。

J.8 Streaming 的實務注意事項

使用 streaming 時,建議注意幾點。

第一,要處理中斷。

使用者可能關閉頁面、取消生成,或網路斷線。
這時候你可能只收到部分文字,所以要決定是否保存 partial message。

第二,要記錄完成狀態。

不要只累積文字,也要知道 response 是否真的 completed。
如果中途 failed,前端應該顯示錯誤狀態。

第三,要處理未知 event。

API 或 SDK 可能回傳你目前沒有特別處理的 event type。
正式產品中可以先記錄這些 event,方便除錯。

第四,要區分「顯示用內容」和「最終保存內容」。

串流過程中顯示的是 partial content。
真正保存到資料庫的內容,最好在 response 完成後再標記為 final。

第五,工具呼叫和 streaming 要分階段導入。

先把文字 streaming 做好,再處理 tool calling streaming,會比較容易維護。

J.9 Streaming 小結

整理一下:

  • 非串流模式可以直接讀 response.output_text
  • 串流模式使用 stream=True
  • Responses API streaming 是 event-based。
  • 文字片段通常透過 response.output_text.delta 取得。
  • 需要自己累積 delta 才能得到完整文字。
  • 前端聊天 UI 可以用 streaming 改善等待體驗。
  • 如果搭配 tools,事件處理會更複雜。
  • 分類、資料抽取、批次任務通常不一定需要 streaming。

最簡單的 streaming 文字範例可以記成:

stream = client.responses.create(
    model="gpt-5.5",
    input="請用三句話介紹 Responses API。",
    stream=True,
)

for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="")

簡單來說:

Streaming 的價值不是讓模型回答更準,而是讓使用者更早看到回答開始出現。

下一章會介紹多模態輸入,也就是如何在 Responses API 中處理文字、圖片與檔案。

K. 多模態輸入:文字、圖片與檔案

Responses API 不只可以處理純文字,也可以處理多模態輸入。

所謂多模態,指的是模型輸入不只包含文字,還可以包含圖片、檔案等內容。

例如:

  • 一段文字問題
  • 一張收據圖片
  • 一張票券截圖
  • 一份 PDF 文件
  • 一段使用者上傳的資料

這也是 Responses API 很適合新專案的原因之一:
它把文字、圖片、檔案、工具與輸出格式都放在同一個 response workflow 裡思考。

K.1 最簡單的文字輸入

最基本的 Responses API 請求仍然是文字輸入:

response = client.responses.create(
    model="gpt-5.5",
    input="請用一句話介紹 Responses API。"
)

print(response.output_text)

這種寫法適合:

  • 問答
  • 摘要
  • 翻譯
  • 改寫
  • 標題生成
  • 文章草稿

如果你的輸入只有文字,而且任務也很單純,使用字串 input 就足夠了。

K.2 圖片輸入:input_image

如果模型支援圖片理解,可以在 input 中加入圖片內容。

例如:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "user",
            "content": [
                {
                    "type": "input_text",
                    "text": "請描述這張圖片。"
                },
                {
                    "type": "input_image",
                    "image_url": "https://example.com/photo.jpg"
                }
            ]
        }
    ],
)

print(response.output_text)

這裡同一則 user input 中包含兩個 content parts:

  • input_text:文字指令
  • input_image:圖片輸入

模型會根據文字指令與圖片內容一起產生回答。

圖片輸入適合:

情境用途
收據照片整理店名、日期、金額
票券截圖抽取班次、時間、地點
UI 截圖分析畫面或錯誤訊息
商品圖片描述商品特徵
旅遊照片產生描述或整理素材
圖表截圖協助說明圖表內容

K.3 圖片 URL 與 base64

圖片可以透過 URL 傳入,也可以用 base64 data URL 的方式傳入。

URL 形式:

{
    "type": "input_image",
    "image_url": "https://example.com/receipt.jpg"
}

base64 data URL 形式概念如下:

{
    "type": "input_image",
    "image_url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..."
}

實務上可以這樣選:

圖片來源建議
圖片已經有可讀取 URL使用 image_url
圖片是使用者剛上傳,不想公開 URL轉成 base64 data URL
圖片在私有儲存空間使用短效 signed URL 或後端轉 base64
圖片很大先壓縮或縮圖

不要把過大的圖片直接丟進 API。
圖片越大,處理成本與延遲通常也會增加。

K.4 檔案輸入:input_file

如果要讓模型處理文件,可以使用 input_file

常見做法是先把檔案上傳到 OpenAI Files API,取得 file_id,再把 file_id 放進 Responses API 的 input。OpenAI 官方範例也示範了先用 client.files.create(..., purpose="user_data") 上傳 PDF,再用 type: "input_file" 與 file_id 作為 Responses API 輸入。OpenAI Developer Community

概念範例:

file = client.files.create(
    file=open("document.pdf", "rb"),
    purpose="user_data"
)

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "user",
            "content": [
                {
                    "type": "input_file",
                    "file_id": file.id
                },
                {
                    "type": "input_text",
                    "text": "請整理這份文件的三個重點。"
                }
            ]
        }
    ],
)

print(response.output_text)

檔案輸入適合:

  • PDF 摘要
  • 文件重點整理
  • 報告初步分析
  • 合約條款摘要
  • 文件轉結構化資料
  • 使用者上傳資料的單次理解

K.5 file input 和 file search 的差異

這裡要特別區分 input_file 和 file_search

它們看起來都和檔案有關,但用途不同。

需求建議
這次請求附上一份文件,請模型讀它input_file
在一批文件或知識庫中檢索相關內容file_search
建立長期可查詢文件庫file_search / vector store / RAG
單次 PDF 摘要input_file
公司知識庫問答file_search

簡單來說:

input_file 比較像「這次請求附上一份文件」。
file_search 比較像「從一批文件中找相關內容」。

如果你只是要模型看一份使用者剛上傳的 PDF,input_file 很直覺。
如果你要讓模型從大量文件中查答案,就應該用 file search,而不是每次把所有文件塞進 input。

K.6 多模態搭配 Structured Outputs

多模態輸入通常會搭配結構化輸出。

例如,使用者上傳收據圖片,你不只是希望模型描述圖片,而是希望它回傳固定欄位:

  • merchant
  • date
  • currency
  • total

概念範例:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "developer",
            "content": "你是收據資料整理助理。看不清楚的欄位請填 null。"
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "input_text",
                    "text": "請讀取這張收據,整理出店名、日期、幣別與總金額。"
                },
                {
                    "type": "input_image",
                    "image_url": "https://example.com/receipt.jpg"
                }
            ]
        }
    ],
    text={
        "format": {
            "type": "json_schema",
            "name": "receipt_summary",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "merchant": {
                        "type": ["string", "null"]
                    },
                    "date": {
                        "type": ["string", "null"]
                    },
                    "currency": {
                        "type": ["string", "null"]
                    },
                    "total": {
                        "type": ["number", "null"]
                    }
                },
                "required": ["merchant", "date", "currency", "total"],
                "additionalProperties": False
            }
        }
    },
)

print(response.output_text)

這樣可以把圖片理解和資料抽取結合起來。

對 TallyTrip 這類旅遊分帳工具來說,流程可能是:

  1. 使用者上傳收據圖片。
  2. Responses API 讀取圖片。
  3. 模型依 JSON Schema 抽取欄位。
  4. 後端驗證金額、幣別與日期。
  5. 建立一筆「待確認」花費草稿。
  6. 使用者確認後才正式寫入帳務。

K.7 多模態輸入不等於資料一定正確

多模態很方便,但不能把它當成百分之百可靠的資料來源。

例如收據圖片可能有:

  • 反光
  • 模糊
  • 遮擋
  • 低解析度
  • 多種語言
  • 手寫備註
  • 幣別不明
  • 小數點或稅額不清楚

模型可能能讀出大部分資訊,但仍可能出錯。

因此在正式產品中,尤其是帳務、付款、法律、醫療、合約等高精確度場景,應該加入:

  • OCR 或圖片品質檢查
  • 後端欄位 validation
  • 金額合理性檢查
  • 幣別限制
  • 使用者確認
  • 錯誤回報與修正流程

多模態輸入可以降低使用者輸入成本,但不應該取代驗證流程。

K.8 什麼時候該用多模態?

可以用這張表判斷:

情境建議
使用者輸入就是文字使用文字 input
要分析圖片內容使用 input_image
要整理收據、票券、截圖使用 input_image,必要時搭配 structured output
要處理單份文件使用 input_file
要查多份文件或知識庫使用 file_search
要產生可解析資料搭配 JSON Schema
高精度帳務或付款資料必須加入驗證與使用者確認

如果任務可以用純文字完成,就不需要硬上多模態。
多模態適合用在輸入本來就不是文字,或使用者不方便手動轉成文字的情境。

K.9 多模態輸入小結

整理一下:

  • Responses API 可以處理文字、圖片與檔案輸入。
  • 純文字任務使用字串 input 就夠。
  • 圖片可以用 input_image
  • 檔案可以用 input_file
  • 單次文件理解適合 file input。
  • 大量文件檢索適合 file search。
  • 多模態輸入常搭配 Structured Outputs。
  • 高精度資料仍要做後端驗證與使用者確認。

簡單來說:

多模態讓 Responses API 不只處理使用者輸入的文字,也能理解圖片、截圖、收據與文件,讓 AI 功能更接近真實產品情境。

下一章會介紹 Conversation state,也就是 Responses API 在多輪對話與狀態管理上的思考方式。

L. Conversation state:狀態管理與多輪對話

做 AI 聊天或助理功能時,最常遇到的問題之一是:模型要怎麼記得前面講過什麼?

在 Chat Completions API 裡,常見做法是由應用程式自己保存 messages,每次呼叫 API 時把需要的歷史對話一起傳入。

例如:

messages = [
    {
        "role": "developer",
        "content": "請使用繁體中文回答。"
    },
    {
        "role": "user",
        "content": "什麼是 Responses API?"
    },
    {
        "role": "assistant",
        "content": "Responses API 是 OpenAI 用來建立模型回應的介面。"
    },
    {
        "role": "user",
        "content": "那它和 Chat Completions 有什麼不同?"
    }
]

這種方式很直接,但也代表你要自己處理:

  • 對話歷史保存
  • token 長度控制
  • 舊對話摘要
  • 使用者 session
  • 多輪上下文
  • 敏感資訊過濾

Responses API 的設計則更強調 response workflow 與 conversation state,可以讓多輪對話和前後 response 的銜接更自然。

L.1 Chat Completions:手動管理 messages

在 Chat Completions API 裡,模型不會自動記得前一次請求。

如果你第一輪問:

什麼是 Responses API?

模型回答後,第二輪你問:

那它和 Chat Completions 有什麼不同?

如果你沒有把前一輪對話一起傳入,模型不一定知道「它」指的是 Responses API。

所以你通常要自己組 messages:

messages = [
    {
        "role": "developer",
        "content": "請使用繁體中文回答。"
    },
    {
        "role": "user",
        "content": "什麼是 Responses API?"
    },
    {
        "role": "assistant",
        "content": "Responses API 是 OpenAI 用來建立模型回應的介面。"
    },
    {
        "role": "user",
        "content": "那它和 Chat Completions 有什麼不同?"
    }
]

completion = client.chat.completions.create(
    model="gpt-5.5",
    messages=messages,
)

這代表 Chat Completions 的多輪對話主要靠應用程式維護。

優點是控制權很高。
缺點是每個專案都要自己處理上下文管理。

L.2 Responses API:可以用 previous_response_id 延續上下文

Responses API 提供了更直接的方式來延續前一次 response 的上下文。

概念上,你可以在下一次請求中指定:

previous_response_id=response.id

例如第一輪:

response = client.responses.create(
    model="gpt-5.5",
    input="什麼是 Responses API?"
)

print(response.output_text)

第二輪:

follow_up = client.responses.create(
    model="gpt-5.5",
    previous_response_id=response.id,
    input="那它和 Chat Completions 有什麼不同?"
)

print(follow_up.output_text)

這樣第二輪 response 就可以接續前一次 response 的上下文,不需要你每次手動重組完整歷史訊息。

這是 Responses API 和 Chat Completions 在多輪對話上的重要差異之一。

L.3 previous_response_id 適合什麼情境?

previous_response_id 適合用在你想延續上一輪上下文的情境。

例如:

  • 一般聊天
  • 多輪問答
  • 文件摘要後追問
  • 工具呼叫後追問
  • 使用者針對前一個回答要求補充
  • 使用者要求「再簡短一點」、「換個說法」、「繼續」

範例:

response = client.responses.create(
    model="gpt-5.5",
    input="請幫我寫一段 TallyTrip 的產品介紹。"
)

follow_up = client.responses.create(
    model="gpt-5.5",
    previous_response_id=response.id,
    input="請改成更適合放在首頁 hero section 的版本。"
)

print(follow_up.output_text)

這種寫法比你自己手動把前一輪輸出塞回 input 更簡潔。

L.4 但正式產品仍應該管理自己的 conversation

雖然 previous_response_id 很方便,但正式產品不應該完全依賴模型端狀態。

你仍然應該在自己的系統中管理:

  • user id
  • conversation id
  • session id
  • message log
  • response id
  • request time
  • model
  • usage
  • latency
  • error status
  • prompt version

例如資料表可能會有:

conversations
- id
- user_id
- title
- created_at
- updated_at

conversation_messages
- id
- conversation_id
- role
- content
- response_id
- model
- usage
- created_at

這樣你才能做到:

  • 顯示歷史對話
  • 讓使用者重新開啟對話
  • 支援搜尋與管理
  • 做成本分析
  • 除錯特定回覆
  • 刪除使用者資料
  • 處理資料保留與隱私政策

簡單來說:

previous_response_id 可以幫你延續模型上下文,但不應該取代你自己的產品資料模型。

L.5 不要無限制保留上下文

不管是 Chat Completions 的 messages,還是 Responses API 的 conversation state,多輪對話都要注意上下文膨脹問題。

如果把所有歷史都帶進模型,可能會遇到:

  • token 成本增加
  • latency 增加
  • 模型被舊資訊干擾
  • 回答變得不聚焦
  • 敏感資訊被重複帶入
  • 超過 context window

實務上通常會做幾件事:

策略說明
保留最近幾輪只保留最近相關對話
摘要舊對話把長歷史壓成短摘要
分 session 管理不同任務分成不同 conversation
過濾敏感資料不必要的個資不要反覆帶入
設定 token 上限控制成本與延遲
保存原始紀錄在自己的資料庫保存完整歷史,但不一定每次都送給模型

這些策略和 API 端點無關,是所有 AI 聊天產品都需要面對的問題。

L.6 Conversation state 和工具呼叫

當你使用 tools 時,狀態管理會更重要。

例如使用者問:

這趟東京旅行目前總共花了多少?

模型可能呼叫工具查詢旅程花費。
下一輪使用者接著問:

那 Ben 還要補多少?

這裡的「Ben」和「那」都依賴前一輪工具結果。

如果你沒有正確保存或延續上下文,模型可能不知道 Ben 是誰,也不知道前一輪查的是哪趟旅程。

因此,工具呼叫場景通常需要同時記錄:

  • 使用者原始問題
  • 模型工具呼叫
  • 工具參數
  • 工具回傳結果
  • 模型最後回答
  • response id
  • conversation id

這樣後續追問才能穩定運作。

L.7 Conversation state 和隱私

狀態管理也會牽涉隱私與資料保留。

例如你的產品可能處理:

  • 使用者旅程
  • 收據
  • 付款狀態
  • 同行旅伴名稱
  • 訂位資訊
  • 上傳文件
  • 個人偏好

這些資料不一定都要長期保留,也不一定每次都要帶進模型請求。

正式產品中應該先設計:

  • 對話紀錄保存多久
  • 使用者能否刪除對話
  • 哪些資料要遮罩
  • 哪些資料只保存在你自己的資料庫
  • 哪些資料可以送進模型
  • log 裡是否保存完整內容
  • response id 和 user id 如何對應

不要等產品上線後才思考資料保留問題。

L.8 Conversation state 小結

整理一下:

面向Chat Completions APIResponses API
多輪對話通常手動保存 messages可用 previous_response_id 延續上下文
控制權應用程式完全控制API 提供更方便的上下文銜接
正式產品資料庫仍然需要仍然需要
token 管理應用程式負責仍需規劃上下文策略
適合情境舊聊天架構、完全自管 messages新專案、多輪 response workflow

Responses API 的 conversation state 能讓多輪對話更容易銜接,但它不是完整產品資料庫,也不是隱私與資料保留策略的替代品。

實務建議是:

  • 簡單追問可以使用 previous_response_id
  • 正式產品仍要保存自己的 conversation / message log。
  • 多輪工具呼叫要記錄工具結果與 response id。
  • 不要無限制保留或傳送所有上下文。
  • 敏感資料要有遮罩、刪除與保存策略。

簡單來說:

Responses API 讓延續上下文更方便,但產品端仍然要負責 conversation 設計、資料保存、權限與成本控制。

下一章會回到實務選型:什麼情況該用 Responses API?什麼情況可以繼續保留 Chat Completions API?

M. 什麼情況該用 Responses API?什麼情況保留 Chat Completions?

看完前面的章節後,你可能會想問:

既然 Responses API 是新方向,那是不是所有 Chat Completions 專案都要立刻遷移?

我的答案是:不需要。

如果既有 Chat Completions 專案已經穩定運作,功能清楚、成本可控、測試完整,就不需要為了追新而馬上重寫。

但如果你正在建立新功能,尤其是需要工具、多模態、file search、web search、structured output 或更完整的 response workflow,Responses API 會是更值得優先考慮的選擇。

這一章會用實務角度整理兩者該怎麼選。

M.1 新專案:優先考慮 Responses API

如果你現在從零開始建立新的 OpenAI API 功能,我會建議優先看 Responses API。

原因是它更符合 OpenAI API 目前的設計方向,也更適合把多種能力放在同一個流程中:

  • 文字輸入
  • 圖片輸入
  • 檔案輸入
  • 結構化輸出
  • 自訂 tools
  • 內建 tools
  • streaming
  • conversation state

如果你的產品未來可能會從「單純聊天」擴展到「工具呼叫、文件檢索、多模態輸入、結構化資料」,一開始用 Responses API 會比較好延伸。

例如你要做:

  • AI 客服助理
  • 文件問答
  • 旅程助理
  • 收據整理
  • 分帳助理
  • 內部知識庫問答
  • agent-like workflow

這些都很適合優先考慮 Responses API。

M.2 既有 Chat Completions 專案:穩定就先保留

如果你已經有一套 Chat Completions 功能,而且目前運作穩定,不需要急著遷移。

例如:

  • 基本聊天功能已經完成
  • Tool Calling 流程已經穩定
  • Streaming UI 已經做完
  • 使用者體驗正常
  • 成本與 token usage 可控
  • log、錯誤處理與測試都已經建立

這種情況下,直接重寫成 Responses API 不一定會立刻帶來明顯收益,反而可能引入新的 bug。

尤其是以下功能,遷移成本可能比較高:

  • 多輪對話
  • streaming UI
  • tool calling
  • structured output
  • request log
  • analytics
  • 測試案例
  • 權限與資料保存流程

如果沒有明確需求,不要只因為新 API 出現就整批重寫。

比較好的做法是:

舊功能穩定維護,新功能優先 Responses API。

M.3 需要內建工具時:優先 Responses API

如果你需要 OpenAI 內建工具,Responses API 會更適合。

例如:

  • web search
  • file search
  • code interpreter
  • 其他工具工作流

這類功能在 Responses API 的設計裡更自然,因為它本來就比較偏向 response workflow,而不是單純產生 assistant message。

適合情境:

需求建議
需要搜尋公開網路資訊Responses API
需要從文件庫找答案Responses API + file search
需要分析檔案或資料Responses API + code interpreter 類工具
需要工具與模型輸出整合Responses API

如果你的應用需要「查資料、再回答」,Responses API 通常會比自己在 Chat Completions 外面拼一堆流程更自然。

M.4 需要多模態輸入時:優先 Responses API

如果你的輸入不只是文字,而是包含圖片、檔案或其他內容,也建議優先看 Responses API。

例如:

  • 使用者上傳收據圖片
  • 使用者上傳票券截圖
  • 使用者上傳 PDF
  • 使用者附上一份旅程文件
  • 使用者希望模型根據圖片與文字一起回答

Responses API 的 input 更適合承載這些多模態內容。

例如 TallyTrip 這類產品可能會需要:

  • 讀取收據照片
  • 整理訂位截圖
  • 分析旅程文件
  • 根據 PDF 產生行程摘要
  • 把圖片內容轉成花費草稿

這些情境比單純聊天更像「多模態 response workflow」,因此 Responses API 會更自然。

M.5 只是簡單聊天:兩者差異不一定大

如果你的功能只是簡單聊天,兩者差異其實不一定很大。

例如:

response = client.responses.create(
    model="gpt-5.5",
    input="請用一句話介紹 Responses API。"
)

和:

completion = client.chat.completions.create(
    model="gpt-5.5",
    messages=[
        {
            "role": "user",
            "content": "請用一句話介紹 Chat Completions API。"
        }
    ],
)

在使用體驗上都很接近。

所以,如果你已經有穩定的 Chat Completions 聊天功能,不需要因為簡單聊天就立刻遷移。

但如果是新專案,仍然可以直接用 Responses API,避免之後要再切換心智模型。

M.6 需要 OpenAI-compatible API:可能仍要理解 Chat Completions

很多第三方服務或 OpenAI-compatible API 仍然優先支援 Chat Completions 格式。

例如某些:

  • LLM gateway
  • self-hosted model server
  • 第三方模型平台
  • proxy API
  • 舊版 SDK
  • 開源工具套件

可能仍然使用:

/v1/chat/completions

或要求你提供:

messages

因此,如果你的產品需要相容多個模型供應商,Chat Completions API 仍然值得理解。

這也是為什麼前面的 Chat Completions 系列仍然有價值。
它不只是舊 API,而是目前整個 LLM 生態中仍然很常見的對話格式。

M.7 決策表:該用哪一個?

可以用這張表快速判斷:

情境建議
從零開始的新 OpenAI API 功能優先 Responses API
既有 Chat Completions 功能穩定先保留
只是簡單聊天且已完成不急著遷移
需要 web search優先 Responses API
需要 file search優先 Responses API
需要多模態輸入優先 Responses API
需要多工具 workflow優先 Responses API
需要 OpenAI-compatible API 相容性仍需理解 Chat Completions
維護舊專案繼續理解 Chat Completions
想統一未來 OpenAI API 開發方式優先 Responses API
要做 agent-like workflow優先 Responses API
已有大量 Chat Completions 測試與 log分階段遷移,不要一次重寫

M.8 我的實務建議

我的建議可以簡化成三句話:

第一,新功能優先 Responses API

如果你現在要做新的 OpenAI API 功能,尤其會牽涉工具、多模態、文件或 structured output,就直接從 Responses API 開始。

第二,舊功能穩定就先保留

如果 Chat Completions 功能已經穩定,沒有明確痛點,不需要急著重寫。

第三,遷移要分階段

不要一次把整個專案從 Chat Completions 改成 Responses API。
先從低風險功能開始,例如摘要、分類、資料抽取,再逐步評估工具呼叫、streaming、多模態與 conversation state。

M.9 本章小結

整理一下:

  • Responses API 更適合新專案。
  • Chat Completions API 仍然適合維護舊專案與相容性情境。
  • 單純聊天兩者都能做,差異不一定大。
  • 需要 tools、web search、file search、多模態、structured output 時,Responses API 優勢更明顯。
  • 已有穩定 Chat Completions 功能時,不需要立刻重寫。
  • 最好的策略通常是:新功能用 Responses API,舊功能分階段評估遷移。

簡單來說:

不要為了追新而重寫穩定功能。
但如果你正在做新功能,Responses API 會是更值得優先選擇的起點。

下一章會接著整理實務遷移建議:如果你已經有 Chat Completions 專案,應該怎麼逐步轉向 Responses API。

N. 實務遷移建議

如果你已經有 Chat Completions API 專案,想逐步轉向 Responses API,最重要的原則是:

不要一次全部重寫。

API 遷移不只是把:

client.chat.completions.create(...)

換成:

client.responses.create(...)

而是會影響輸入格式、輸出讀取、工具呼叫、streaming、log、測試與錯誤處理。

因此,比較穩妥的做法是分階段遷移。

N.1 先盤點現有功能

遷移前,先整理目前專案裡哪些地方使用 Chat Completions API。

可以先分類:

類型範例
純文字任務摘要、翻譯、改寫、標題生成
結構化輸出JSON、分類、資料抽取
Tool Calling查資料庫、查訂單、查旅程
Streaming聊天 UI、長文即時輸出
多模態圖片、音訊、檔案輸入
可觀測性usage log、metadata、user tracking
測試prompt regression、單元測試、整合測試

不要一開始就改最複雜的功能。
先找出風險最低、最容易驗證的部分。

N.2 先從低風險功能開始

最適合第一批遷移的通常是:

  • 摘要
  • 翻譯
  • 改寫
  • 標題生成
  • 簡單分類
  • 單次資料抽取

原因是這些功能通常有幾個特徵:

  • 不依賴多輪對話
  • 不依賴工具呼叫
  • 不需要 streaming
  • 輸入輸出比較單純
  • 容易用測試資料比較結果

例如原本 Chat Completions 寫法:

completion = client.chat.completions.create(
    model="gpt-5.5",
    messages=[
        {
            "role": "developer",
            "content": "請使用繁體中文摘要內容。"
        },
        {
            "role": "user",
            "content": article
        }
    ],
)

summary = completion.choices[0].message.content

可以改成 Responses API:

response = client.responses.create(
    model="gpt-5.5",
    input=[
        {
            "role": "developer",
            "content": "請使用繁體中文摘要內容。"
        },
        {
            "role": "user",
            "content": article
        }
    ],
)

summary = response.output_text

這類遷移相對單純,適合先做。

N.3 再遷移 Structured Outputs

如果你的功能已經使用 Chat Completions 的 response_format,可以再評估遷移到 Responses API 的 text.format

Chat Completions 寫法:

completion = client.chat.completions.create(
    model="gpt-5.5",
    messages=messages,
    response_format={
        "type": "json_schema",
        "json_schema": schema
    },
)

Responses API 寫法概念:

response = client.responses.create(
    model="gpt-5.5",
    input=input_items,
    text={
        "format": {
            "type": "json_schema",
            "name": "structured_result",
            "strict": True,
            "schema": schema
        }
    },
)

這類功能遷移時,除了改 API 呼叫,也要確認:

  • schema 是否一致
  • 輸出 JSON 是否仍能被 parse
  • 後端 validation 是否仍正常
  • 錯誤處理是否一致
  • 測試資料結果是否可接受

Structured Outputs 比純文字任務更適合用測試資料做比較。
你可以準備一批固定輸入,分別用 Chat Completions 和 Responses API 跑一次,再比較欄位完整度、格式穩定性與錯誤率。

N.4 Tool Calling 不要第一個遷移

Tool Calling 通常不適合當第一個遷移目標。

原因是它牽涉的不只是模型輸出,還包含:

  • 工具 schema
  • 工具選擇
  • 工具參數 parse
  • 權限檢查
  • 後端 API 呼叫
  • 工具結果回填
  • 錯誤處理
  • 使用者確認
  • log 與 audit trail

如果原本 Chat Completions 的工具流程已經穩定,建議先保留。

等你熟悉 Responses API 的 input、output、structured output 後,再開始遷移工具流程。

遷移 tools 時,建議先從查詢型工具開始,例如:

  • 查旅程
  • 查花費
  • 查訂單
  • 查文件
  • 查狀態

不要一開始就遷移高風險工具,例如:

  • 刪除資料
  • 發送 email
  • 建立付款
  • 修改帳務
  • 取消訂單

高風險工具應該最後遷移,並且保留使用者確認流程。

N.5 Streaming 要單獨測

Streaming 也不建議和其他遷移一起做。

原因是 Chat Completions API 和 Responses API 的串流模型不同。

Chat Completions streaming 主要處理:

chunk.choices[0].delta.content

Responses API streaming 則是 event-based:

event.type == "response.output_text.delta"

所以如果你的前端聊天 UI 已經依賴原本的 chunk 格式,遷移時要重新檢查:

  • 後端 streaming parser
  • 前端事件格式
  • partial message 狀態
  • 使用者取消生成
  • 錯誤事件
  • completed / failed 狀態
  • usage 或 log 取得方式
  • tool call streaming

比較好的方式是先建立一個新的 Responses API streaming endpoint,和舊 endpoint 平行測試,而不是直接替換正式路徑。

N.6 多模態功能可以優先用 Responses API 開新功能

如果你的舊專案原本沒有多模態功能,例如圖片、檔案、收據或截圖分析,那不一定要把舊功能遷移過來。

可以直接用 Responses API 開新功能。

例如:

  • 收據圖片整理
  • 票券截圖分析
  • PDF 摘要
  • 文件問答
  • 圖片加文字的資料抽取

這類功能本來就比較適合 Responses API 的 input 設計。

也就是說,不必把所有舊功能先遷移完,才開始用 Responses API。
比較實務的做法是:

舊功能繼續維護,新功能直接用 Responses API。

N.7 建立自己的 wrapper

如果你的產品會長期使用 OpenAI API,建議不要讓業務邏輯直接綁死在某個 API 呼叫上。

不要在各處都直接寫:

client.chat.completions.create(...)

或:

client.responses.create(...)

比較好的做法是建立自己的 wrapper,例如:

def generate_text(*, input_text: str, feature: str, user_id: str) -> str:
    response = client.responses.create(
        model="gpt-5.5",
        input=input_text,
        metadata={
            "feature": feature
        },
        user=user_id,
    )

    return response.output_text

或更進一步,把 Chat Completions 和 Responses API 包在同一層介面後面:

def run_ai_task(*, task_type: str, input_data: dict) -> dict:
    if task_type == "legacy_chat":
        return run_chat_completion(input_data)

    return run_response(input_data)

這樣未來如果要切換 API、模型或供應商,就不需要修改整個產品。

Wrapper 裡也可以統一處理:

  • model 選擇
  • metadata
  • user
  • usage log
  • latency
  • error handling
  • retry
  • timeout
  • prompt version
  • schema validation

這會比把 API 呼叫散落在專案各處更好維護。

N.8 建立比較指標

遷移不應該只看「能不能跑」,也要看品質與成本。

建議比較:

指標說明
輸出品質回答是否符合需求
格式穩定性JSON / schema 是否穩定
latency回應速度是否可接受
token usage成本是否增加
錯誤率API 錯誤、parse error、tool error
使用者修正率使用者是否常修改模型結果
工具呼叫成功率tool calls 是否正確
維護成本程式碼是否更簡潔或更複雜

例如收據抽取功能,可以比較:

  • merchant 是否正確
  • date 是否正確
  • currency 是否正確
  • total 是否正確
  • JSON 是否可 parse
  • 使用者是否需要修改欄位

不要只因為 Responses API 是新介面,就假設它在每個任務上一定比較好。
實際效果仍然要用你的資料與產品場景測試。

N.9 建議遷移順序

如果你真的要從 Chat Completions 逐步轉向 Responses API,我會建議這個順序:

1. 新功能先用 Responses API
2. 純文字任務先遷移
3. Structured Outputs 再遷移
4. 多模態新功能直接用 Responses API
5. 查詢型 tools 再遷移
6. Streaming endpoint 單獨測試
7. 高風險 tools 最後遷移
8. 穩定後再考慮整理舊 Chat Completions wrapper

這樣可以降低風險。

不要從最複雜的聊天 UI、tool calling agent 或付款流程開始改。

N.10 遷移小結

整理一下:

  • 不需要一次把所有 Chat Completions 功能改成 Responses API。
  • 先盤點功能類型與風險。
  • 從低風險純文字任務開始。
  • Structured Outputs 可以用測試資料比較結果。
  • Tool Calling 和 Streaming 要分開遷移。
  • 高風險工具最後處理。
  • 多模態新功能可以直接用 Responses API。
  • 建議建立自己的 wrapper,避免業務邏輯綁死 API。
  • 遷移時要比較品質、成本、latency 與錯誤率。

簡單來說:

最好的遷移策略不是「全部重寫」,而是「新功能優先 Responses API,舊功能分階段評估遷移」。

下一章會進入總結,整理這篇文章的核心結論:新專案優先 Responses API,舊專案穩定後再遷移。

O. 總結:新專案優先 Responses API,舊專案穩定後再遷移

這篇文章的重點,不是要說 Chat Completions API 已經沒有價值,也不是要鼓勵你立刻把所有舊功能重寫。

真正想釐清的是:

如果現在要做新的 OpenAI API 功能,應該如何理解 Responses API?
如果已經有 Chat Completions 專案,又該如何判斷是否需要遷移?

簡單來說,我會建議:

  • 新專案優先看 Responses API。
  • 舊專案如果穩定,就先保留 Chat Completions API。
  • 新功能可以直接用 Responses API 開始。
  • 舊功能要遷移時,應該分階段處理,不要一次全部重寫。

O.1 Responses API 的核心心智模型

Chat Completions API 的核心是:

messages -> assistant message

你提供一組 messages,模型回傳下一則 assistant message。

Responses API 的核心則是:

input -> response output

你提供 input,模型建立一個完整 response。
這個 response 可能包含:

  • 一段文字回答
  • 結構化輸出
  • 工具呼叫
  • 多模態輸入的處理結果
  • streaming events
  • conversation state 相關資訊

所以,Responses API 不只是 Chat Completions API 的改名版本。
它更像是把輸入、輸出、工具、多模態與狀態管理整合到同一個 workflow 裡的新介面。

O.2 為什麼新專案優先 Responses API?

如果你現在從零開始做新功能,Responses API 會比較適合作為起點。

原因是新專案通常不只會停留在純文字問答。
未來很可能會需要:

  • Structured Outputs
  • Tool Calling
  • web search
  • file search
  • 圖片輸入
  • 檔案輸入
  • streaming
  • 多輪對話
  • response state
  • usage 與 metadata 追蹤

Responses API 對這些能力的整合更自然,也更符合 OpenAI API 目前的產品方向。

因此,如果你要做:

  • AI 客服
  • 文件問答
  • 旅程助理
  • 收據整理
  • 分帳助理
  • 內部知識庫問答
  • 多工具 agent workflow

我會建議優先從 Responses API 開始。

O.3 為什麼舊專案不用急著遷移?

如果你已經有 Chat Completions API 功能,而且目前運作穩定,就不需要急著重寫。

例如:

  • 聊天功能正常
  • Tool Calling 流程穩定
  • Streaming UI 已完成
  • 成本可控
  • log 與 usage 已經建好
  • 測試案例完整
  • 使用者體驗沒有問題

這種情況下,直接遷移不一定會立刻帶來收益,反而可能引入新的 bug。

Chat Completions API 仍然很值得理解,尤其是:

  • 維護舊專案
  • 閱讀舊教學
  • 使用 OpenAI-compatible API
  • 支援第三方模型 gateway
  • 理解 messages / roles / tool calling 的基礎模型

所以,不要把 Responses API 理解成「Chat Completions API 不能用了」。
更準確的說法是:

Chat Completions API 仍然適合維護既有架構;Responses API 更適合作為新功能與新 workflow 的起點。

O.4 實務選擇原則

可以用這張表快速判斷:

情境建議
從零開始的新功能優先 Responses API
既有 Chat Completions 功能穩定先保留
單純聊天、摘要、改寫兩者都可以
需要 web search / file search優先 Responses API
需要圖片、檔案等多模態輸入優先 Responses API
需要工具與 response workflow 整合優先 Responses API
已有大量 Chat Completions 測試與 log分階段遷移
需要 OpenAI-compatible API 相容性繼續理解 Chat Completions
高風險工具或付款流程不要急著遷移,先完整測試

O.5 遷移時的建議順序

如果你已經決定要從 Chat Completions API 逐步轉向 Responses API,可以照這個順序:

1. 新功能先用 Responses API
2. 純文字任務先遷移
3. Structured Outputs 再遷移
4. 多模態新功能直接用 Responses API
5. 查詢型 tools 再遷移
6. Streaming endpoint 單獨測試
7. 高風險 tools 最後遷移
8. 穩定後再整理舊 Chat Completions wrapper

這樣做的好處是風險比較低。

不要一開始就改最複雜的功能,例如:

  • streaming 聊天 UI
  • 多輪 tool calling agent
  • 寫入型工具
  • 付款或帳務流程
  • 高風險外部操作

這些功能應該等你熟悉 Responses API 的 input、output、tools、streaming 與 state 後,再分階段處理。

O.6 最重要的不是 API 名稱,而是產品邊界

無論你使用 Chat Completions API 還是 Responses API,正式產品都要處理幾件事:

  • 模型輸出是否需要驗證
  • 工具參數是否安全
  • 使用者是否有權限
  • token 成本是否可控
  • request log 是否完整
  • streaming 中斷如何處理
  • 高風險操作是否需要確認
  • 敏感資料是否應該遮罩
  • 使用者資料是否能刪除

換句話說,API 只是介面。
真正決定產品穩定性的,是你怎麼設計邊界、驗證、權限、log、錯誤處理與 fallback。

Responses API 讓很多 workflow 更統一,但它不會自動幫你解決所有產品工程問題。

O.7 最後總結

這篇文章可以用幾句話收束。

第一,Chat Completions API 仍然值得理解,尤其是舊專案、相容性與 messages 心智模型。

第二,Responses API 是新專案更值得優先看的方向,尤其當你需要工具、多模態、structured output、file search、web search 或更完整的 response workflow。

第三,遷移不應該一次全部重寫,而是應該從低風險功能開始,逐步比較品質、成本、latency 與維護成本。

第四,無論使用哪個 API,正式產品都要做好 validation、permission、logging、usage tracking 與 error handling。

簡單來說:

Chat Completions API 是理解 OpenAI 對話模型的重要基礎。
Responses API 則更適合新專案,把輸入、輸出、工具、多模態與狀態管理整合成一個 response workflow。

如果你正在維護舊功能,先讓它穩定運作。
如果你正在建立新功能,就可以從 Responses API 開始。